(ns com.ufo5260987423.graphDatabase.core.clojure.core.gao
  (:use com.ufo5260987423.graphDatabase.core.clojure.core.utils)
  (:import [com.ufo5260987423.graphDatabase.core.GAO
            GraphNodeFactory GraphNode GraphEdge]))

(println "load core.gao")

(defn get-related-graph-node-id [graph-edge-map from-or-to]
  (map #(get (second %) from-or-to) graph-edge-map))

(defn search-graph-edge-by-graph-node-id [graph-node-id graph-edge-map from-or-to]
  (filter #(= graph-node-id (get (second %) from-or-to)) graph-edge-map))

(defn contain-node-id-in-graph-edge-map? [graph-node-id graph-edge-map from-or-to]
  (not (empty? (search-graph-edge-by-graph-node-id graph-node-id graph-edge-map from-or-to))))

(defn filter-graph-edge-id [graph-edge-map]
  (map #(:graph-edge-id %) graph-edge-map))

(defn java-clojure-for-graph-edge [graph-edge-target]
  (graph-edge
    (.getEdgeId graph-edge-target)
    (into {} (.getAttributeMap graph-edge-target))
    (.getFromNodeId graph-edge-target)
    (.getToNodeId graph-edge-target)))

(defn clojure-java-for-graph-edge [graph-edge-target]
  (let [result (new GraphEdge)]
    (.setEdgeId result (:graph-edge-id graph-edge-target))
    (.setAttributeMap result (java.util.HashMap. (:graph-edge-attribute-map graph-edge-target)))
    (.setFromNodeId result (Integer. (:from-node-id graph-edge-target)))
    (.setToNodeId result (Integer. (:to-node-id graph-edge-target)))
    result))

(defn java-clojure-for-graph-node [graph-node-target]
  (graph-node
    (.getGraphNodeId graph-node-target)
    (into {} (.getAttributeMap graph-node-target))
    (reduce #(conj %1 {(first %2) (java-clojure-for-graph-edge (second %2))})
      {} (into {} (.getFromThisNodeEdges graph-node-target)))
    (reduce #(conj %1 {(first %2) (java-clojure-for-graph-edge (second %2))})
      {} (into {} (.getToThisNodeEdges graph-node-target)))))

(defn clojure-java-for-graph-node [graph-node-target]
  (let [result (new GraphNode)]
    (.setGraphNodeId result (Integer. (:graph-node-id graph-node-target)))
    (.setAttributeMap result (java.util.HashMap. (:graph-node-attribute-map graph-node-target)))
    (.setFromThisNodeEdges result
      (java.util.HashMap. (reduce #(conj %1 {(Integer. (first %2)) (clojure-java-for-graph-edge (second %2))}) {}
        (:from-this-node-edges-map graph-node-target))))
    (.setToThisNodeEdges result
      (java.util.HashMap. (reduce #(conj %1 {(Integer. (first %2)) (clojure-java-for-graph-edge (second %2))}) {}
        (:to-this-node-edges-map graph-node-target))))
    result))

(def graph-node-factory (new GraphNodeFactory))
(def graph-node-id-record (ref (.getBiggestId graph-node-factory)))

(defn get-graph-node-by [graph-node-id]
  (if (nil? graph-node-id)
    nil
    (java-clojure-for-graph-node (.getGraphNodeBy graph-node-factory (Integer. graph-node-id)))))

(defn save-graph-node [graph-node]
  (.saveGraphNode graph-node-factory (clojure-java-for-graph-node graph-node)))

(defn delete-graph-node [graph-node]
  (.deleteGraphNode (clojure-java-for-graph-node graph-node)))

(defn save-graph-node-attribute-name [name]
  (.addAttributeName graph-node-factory name))

(defn save-graph-edge-attribute-name [name]
  (.addAttributeName (.getGraphEdgeFactory graph-node-factory) name))

(defn get-new-graph-node-id []
  (Integer.
    (dosync (ref-set graph-node-id-record (inc @graph-node-id-record))
      @graph-node-id-record)))

(defn reverse-edge-key [target]
  (cond (= :from-this-node-edges-map target) :to-this-node-edges-map (= :to-this-node-edges-map target) :from-this-node-edges-map (= :from-node-id target) :to-node-id (= :to-node-id target) :from-node-id))

(defn save-graph-edge [graph-edge]
  (let [graph-edge-old (.getGraphEdgeWith
                         (.getGraphEdgeFactory graph-node-factory)
                         (Integer. (:from-node-id graph-edge))
                         (Integer. (:to-node-id graph-edge)))]
    (.saveGraphEdge
      (.getGraphEdgeFactory graph-node-factory)
      (clojure-java-for-graph-edge
        (conj graph-edge
          {:graph-edge-id
           (if (nil? graph-edge-old)
             nil
             (.getEdgeId graph-edge-old))
           })))))

(defn delete-graph-edge [from-node-id to-node-id]
  (if (and (nil? from-node-id) (nil? to-node-id))
    nil
    (.deleteGraphEdge
      (.getGraphEdgeFactory graph-node-factory)
      (.getGraphEdgeWith (.getGraphEdgeFactory graph-node-factory) (Integer. from-node-id) (Integer. to-node-id)))))

(defn search-node-by-attribute [attribute-name attribute-value]
  (map java-clojure-for-graph-node (.searchByNodeAttribute graph-node-factory attribute-name attribute-value)))